دورة البايثون للأستاذ سليمان القسيمي - ملخص الدرس التاسع : الدوالي و القواميس :
السلام عليكم و رحمة الله و بركاته و طابت أوقاتكم بكل خير و سعادة .
أعتقد أنكم تتذكرون قصة المتوسط الحسابي التي درسناها في دروس سابقة ، ما رأيكم لو جعلنا كل أكواد التعرف على المتوسط الحسابي لقائمة ما في دالة ؟ 
يعني ننشىء دالة خاصة بنا نسميها " average"تكون مهمتها هي إحتساب المتوسط الحسابي لأي قائمة نضعها كمعامل في دالتنا ؟ 
نعم هذا ممكن و سيوفر لنا الكثير من الجهد و الأسطر البرمجية و الوقت أيضا .
تصبح لنا دالة جاهزة و يكفي وضع قائمة ما كمعامل لهذه الدالة لتعطينا بكل سرعة المتوسط الحسابي لتلك القائمة .
إلى العمل البرمجي مباشرة :
def average(l):


    s = sum(l)
    length = len(l)
    return s/length
    
l = [10,20,56,42,89,36,47,21]
av = average(l)
av = round(av,2)
و لطباعة المتوسط الحسابي لتلك القائمة نكتب :
print(av)
هل تأكدتم الآن أن إنشاء الدالة ممكن و بسيط و مفيد ؟ 

و ممكن أن ننشىء دالة أخرى تقوم بجمع عددين :
def plus(x,y):
    return x+y
plus(5,10)

و هذه دالة ثالثة تقوم باحتساب مربع كل عدد تضعونه كمعامل لها :
def square(x):
    return x*x
square(5)

و يمكنكم التفكير في ما شئتم من دوالي لتستخدمونها في برامجكم .

الأخطاء في البرمجة : 
توجد 3 أنواع رئيسية من الأخطاء :
الخطأ الأول :الخطأ القاعدي و هو الخطأ الذي يمس قواعد لغة البرمجة  مثال : فتح تنصيص ثم عدم إغلاقه أو عدم إغلاق قوس أو عدم وضع : في نهاية سطر if أو for ...والامثلة كثيرة من هذا النوع من الأخطاء .
و هذه الأخطاء يكتشفها البايثون مباشرة و لا يشتغل البرنامج و هي أخطاء تسمى syntax errors
النوع الثاني من الأخطاء هي run time errors و هي الأخطاء التي تحدث أثناء التشغيل بسبب حدوث أشياء غير متوقعة ، لنأخذ مثال تمرين الآلة الحاسبة :
while True:
    x = int(input("أدخل الرقم الأول:\n"))
    o = input("أدخل أحد علامات العمليات الحسابية +,-,*,/ : \n")
    y = int(input("أدخل الرقم الثاني:\n"))
    if o=='+':
        print(f"{x} + {y} = {x+y}")
    elif o=='*':
        print(f"{x} * {y} = {x*y}")
    elif o=='-':
        print(f"{x} - {y} = {x-y}")
    elif o=='/':
        print(f"{x} / {y} = {x/y}")
    else:
        print("حصل خطأ في إدخال العلامة الحسابية ")

    ask = input("هل ترغب في إجراء عمليات حسابية أخرى؟ (أجب ب y أو n) :")
    if ask == 'y':
        continue
    else:
        break
        البرنامج يشتغل بشكل طبيعي و يقوم بكل العمليات الحسابية التي نطلبها منه .
        مثال لو أردنا إجراء العملية : 5 /0 أي نقسم 5 على 0 ، حينها يتوقف البرنامج و يعطينا رسالة الخطأ التالية :
ZERODivisionError: division by zero
و هذا الخطأ لا علاقة له بالكود و لا بالبرمجة بل هو خطأ رياضي بحت ففي الرياضيات لا وجود للقسمة على صفر .
و على المبرمج توقع حصول هذه الأخطاء و معالجتها بكتابة كوده بشكل يسمح للبرنامج بتجاوز هذه الأخطاء و ضمان عدم توقف البرنامج بسببها .
في برنامج الآلة الحاسبة أين يمكن توقع حصول أخطاء؟ و الجواب : عند إجراء عملية القسمة على صفر .
إذن الحل هو أن نطلب من البرنامج أن لا يقوم بعملية القسمة مباشرة ، بل نقل له : تريّث حبّة حبّة ....حاول أن تقوم بعملية القسمة ، فإن كانت ممكنة واصل عملك ، و إن كانت غير ذلك أي لا يمكن القيام بها فنطلب منه أمرا مختلف فلا يواصل في اجراء العمليةو يتوقف البرنامج معلنا حدوث خطأ .
هذا الإجراء يسمى كود الإستثناء أي أن نستثني خطأ ما 
و يوجد خطأ آخر معروف و هو ValueError: و يظهر هذا الخطأ ، مثلا، عندما يدخل المستخدم شيئا مختلفا عن الأرقام حين يكون مدعوا لإدخل رقم .
و في هذا الإصلاح الموالي لكودنا للآلة الحاسبة سنتجاوز هذه الأخطاء المتوقع حدوثها بواسطة try ....except :

while True:
    x = input("أدخل الرقم الأول:\n")
    o = input("أدخل أحد علامات العمليات الحسابية +,-,*,/ : \n")
    y = input("أدخل الرقم الثاني:\n")
    try:
        x = int(x)
        y = int(y)
    except ValueError:
        print("أدخل أرقاما فقط")
        continue

    if o=='+':
        print(f"{x} + {y} = {x+y}")
    elif o=='*':
        print(f"{x} * {y} = {x*y}")
    elif o=='-':
        print(f"{x} - {y} = {x-y}")
    elif o=='/':
        try:
            print(f"{x} / {y} = {x/y}")
        except ZeroDivisionError:
            print("القسمة على 0 غير ممكنة")
    else:
        print("حصل خطأ في إدخال العلامة الحسابية ")

    ask = input("هل ترغب في إجراء عمليات حسابية أخرى؟ (أجب ب y أو n) :")
    if ask == 'y':
        continue
    else:
        break
النوع الثالث من الأخطاء هي الأخطاء المنطقية و هي أخطاء يرتكبها المبرمج، مع هذا النوع من الأخطاء البرنامج يشتغل و لكن نتيجة التنفيذ تكون خاطئة أو غير منطقية أصلا .

القواميس :
أخترت لكم هذه المعلومات من موقع "موسوعة حاسوب" و بعد هذه الخلاصة المختصرة ستجدون الكود الذي شرحه الأستاذ سليمان في الدرس التاسع .
القواميس هي مجاميع غير مرتّبة من أزواج (مفتاح: قيمة) (key: value) مع اشتراط كون المفاتيح ذات قيم فريدة (ضمن القاموس الواحد). يمكن استخدام الأقواس المعقوفة {} لإنشاء قاموس فارغ.
مثال لقاموس :
tel = {'jack': 4098, 'sape': 4139}
يمكن إضافة بيانات : 
tel['guido'] = 4127
فيصبح القاموس هكذا :
tel{'sape': 4139, 'guido': 4127, 'jack': 4098}

تأخذ القواميس مسمّيات مختلفة في لغات البرمجة الأخرى، مثل الذواكر الترابطية (associative memories) أو المصفوفات الترابطية (associative arrays). 
تختلف القواميس عن أنواع التسلسلات الأخرى في طريقة الفهرسة، فالقوائم والمجموعات تكون مفهرسة بواسطة مدى محدد من الأرقام، في حين أنّ القواميس تكون مفهرسة بواسطة المفاتيح (keys). 
يمكن استخدام أي نوع من أنواع البيانات غير القابلة للتغيير (immutable) كمفاتيح في القواميس، مثل السلاسل النصية والأرقام، أو الصفوف التي تتضمن سلاسل نصية أو أعداد أو صفوف، ولكن بشرط أن لا تتضمن - بصورة مباشرة أو غير مباشرة - أي كائن قابل للتغيير. وبطبيعة الحال لا يمكن استخدام القوائم كمفاتيح نظرًا لأنّها كائنات قابلة للتغيير بواسطة الإسناد عن طريق الفهارس أو الاقتطاع أو توابع مثل append() و extend(). 
في حال إضافة قائمة بأزواج (مفتاح: قيمة) داخل الأقواس المعقوفة فإنّ اللغة تضيف هذه الأزواج كقيمة ابتدائية في القاموس، وتستخدم اللغة طريقة العرض هذه عندما تعرض القواميس كمخرجات. 
إنّ العمليات الرئيسية التي تؤديها القواميس هي تخزين قيمة مع مفتاح معين، واستخراج تلك القيمة بواسطة المفتاح الخاصّ بها. 
يمكن حذف زوج (مفتاح: قيمة) باستخدام الكلمة المفتاحية del:
>>> del tel['sape']
في حال تخزين قيمة باستخدام مفتاح مستخدم سابقًا، فإنّ القيمة السابقة لذلك المفتاح ستهمل وستربط اللغة القيمة الجديدة بهذا المفتاح.
>>> tel['jack']
4098
>>> tel['jack'] = 9841
>>> tel['jack']
9841
تطلق اللغة خطأ KeyError في حال استخراج قيمة باستخدام مفتاح غير موجود ضمن القاموس:
>>> tel['rosy']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'rosy'
يمكن الحصول على قائمة عشوائية بالمفاتيح الموجودة ضمن القاموس باستخدام الدالة list():
>>> list(tel.keys())
['jack', 'guido']
وللحصول على قائمة مرتّبة بالمفاتيح فيمكن استخدام الدالة sorted():
>>> sorted(tel.keys())
['guido', 'jack']
للتحقق من وجود مفتاح معين في القاموس يمكن استخدام الكلمة المفتاحية in:
>>> 'guido' in tel
True
>>> 'jack' not in tel
False
تستخدم الدالة dict() لإنشاء قواميس بصورة مباشرة من تسلسلات من أزواج (مفتاح، قيمة)، كما يلي:
>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'jack': 4098, 'guido': 4127}
يمكن كذلك الاستفادة من dict comprehensions لإنشاء قواميس من عبارات (مفتاح: قيمة) متنوعة:
>>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}
عندما تكون المفاتيح سلاسل نصية بسيطة، يمكن استخدام معاملات مفتاحية لتعيين الأزواج، كما في المثال التالي:
>>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'jack': 4098, 'guido': 4127}
الدوال التابعة للكائن dict
dict.clear()
تحذف الدالة جميع عناصر القاموس. 
dict.copy()
تؤدي الدالة عملية نسخ سطحية (shallow copy) للقاموس. 
dict.fromkeys()
تنشئ الدالة قاموسًا جديدًا من التسلسل الذي يحدّده المستخدم في المعاملات. 
dict.get()
تعيد الدالة القيمة المرتبطة بالمفتاح الذي يحدّده المستخدم. 
dict.items()
تعيد الدالة عرضًا جديدًا لعناصر القاموس بهيئة (مفتاح، قيمة). 
dict.keys()
تعيد الدالة عرضًا جديدًا يتضمّن جميع مفاتيح القاموس. 
dict.pop()
تحذف الدالة المفتاح المحدد في المعاملات من القاموس. 
dict.popitem()
تحذف الدالة زوج (مفتاح، قيمة) عشوائيًا من القاموس. 
dict.setdefault()
تعيد الدالة قيمة المفتاح المحدد في المعاملات إن كان موجودًا. أما إن كان المفتاح غير موجود في القاموس فإنّ الدالة تضيف المفتاح مع القيمة المحدّدة له إلى القاموس. 
dict.update()
تحدّث الدالةُ القاموسَ بزوج (مفتاح، قيمة) من قاموس أو كائن آخر قابل للتكرار. 
dict.values()
تعرض الدالة قيم المفاتيح الخاصة بالقاموس. 

و هذا كود البرنامج الذي شرحه الأستاذ سليمان في الدرس التاسع :
import webbrowser


urls = {}

print("مرحبًأ بك في مخزن الروابط")
while True:
	option = input("اكتب الرقم 1 إذا كنت ترغب في فتح واحد من الروابط المضافة, أو اضغط 2 إذا كنت تريد إضافة رابط جديد")
	if option == "1":
		keys = list(urls.keys())
		print("الروابط المتوفرة: ")
		if keys == []:
			print("لا توجد عناصر مضافة")
		else:
			for key in keys:
				print(key)
			name = input("اكتب اسم الموقع الذي تريد فتحه")
			try:
				link = urls[name]
			except KeyError:
				print("هذا الموقع غير موجود")
				continue
			webbrowser.open(link)
	elif option == "2":
		key = input("اكتب الاسم الظاهري للموقع الذي تريد إضافته")
		link = input("الصق الرابط هنا")
		urls[key] = link
